---
title: Authentication and authorization
summary: Identity establishment, authorization enforcement, and per-operation access requirements.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-09-22
---

# Authentication and authorization

Identity is established, where it is established at all, entirely by IIS at the transport layer; nothing in any of the six service classes carries an authorization attribute or a programmatic role check. `SecurityService` implements the authorization *provider* used by other SAIF applications (`IsAuthorized`/`IsAuthorized2`), but that authorization logic protects only the resources those callers separately guard — it does not gate any operation on this host itself.

## Authentication

| Aspect | Finding |
| --- | --- |
| Mechanism | IIS-configured Windows and/or anonymous authentication, varying by deployment variant |
| Transport security | Not declared in `system.serviceModel` bindings for the inbound services — default `basicHttpBinding` security mode |
| Client credential type | Not declared for inbound services |
| Established by | IIS site/application configuration — `IISWindowsAuthInternal`/`IISAnonymousAuthInternal` (Internal) and `IISWindowsAuthExternal`/`IISAnonymousAuthExternal` (External), values defined outside this repository |
| Identity type | `WindowsIdentity`, where Windows authentication is enabled |
| Configured in | `pipelines/release.yml` (per-variant IIS authentication switches) |
| Anonymous access | Varies by deployment variant and environment — governed by pipeline variables not present in this repository |

No service class, operation, or binding in this repository declares an authentication requirement in code; whatever a caller must present to reach an operation is entirely a function of the IIS site configuration applied at deploy time for the Internal or External variant. See [Activation and hosting](activation-hosting.md) for the variant mapping.

## Caller context

| Aspect | Finding |
| --- | --- |
| Context source | `OperationContext.Current.RequestContext` (five of six services); `HttpContext.Current` (`SecurityService.LogRequestDetails`, `PreferenceService.GetDefaultPrinter`) |
| Consumed by | `WcfEnvironmentService` constructor argument in `FaxService`, `PreferenceService`, `WebContextService`, `ImagingService`, `SecurityService` |
| Identity resolved to | Environment track and per-track configuration access — not a caller identity; no operation resolves a caller's Windows or application identity for authorization purposes |
| Behavior without context | Construction fails — all five services that build `WcfEnvironmentService` from `OperationContext.Current.RequestContext` are constructible only inside an active WCF operation context |

## Authorization enforcement

| Mechanism | Scope | Declared in | Effect |
| --- | --- | --- | --- |
| None in code | — | — | Every dispatched message reaches its operation; access relies entirely on IIS authentication and network placement for the deployed variant |

No service class in this host carries an authorization attribute, and no operation performs a programmatic role or permission check before executing its body. This is true of all 24 operations across all six services, including the five unconstrained `FileService` path operations and the ten `SecurityService` identity/authorization-provider operations. `SecurityService.IsAuthorized`/`IsAuthorized2` compute an authorization *answer* for other applications to act on; they do not gate access to `SecurityService` itself or to any other operation on this host.

## Access requirements by operation

| Operation | Requirement | Enforced by | On failure |
| --- | --- | --- | --- |
| [`ReadFile`](contracts/file-service.md#readfile) | None in code | — | Call proceeds |
| [`ReadEntireFile`](contracts/file-service.md#readentirefile) | None in code | — | Call proceeds |
| [`WriteFile`](contracts/file-service.md#writefile) | None in code | — | Call proceeds |
| [`DeleteFile`](contracts/file-service.md#deletefile) | None in code | — | Call proceeds |
| [`Length`](contracts/file-service.md#length) | None in code | — | Call proceeds |
| [`GetPreferences`](contracts/preference-service.md#getpreferences) | None in code | — | Call proceeds |
| [`SavePreferences`](contracts/preference-service.md#savepreferences) | None in code | — | Call proceeds |
| [`GetDefaultPrinter`](contracts/preference-service.md#getdefaultprinter) | None in code | — | Call proceeds |
| [`Get`](contracts/web-context-service.md#get) | None in code | — | Call proceeds |
| [`Save`](contracts/web-context-service.md#save) | None in code | — | Call proceeds |
| [`CreateContext`](contracts/web-context-service.md#createcontext) | None in code | — | Call proceeds |
| [`QueueImage`](contracts/imaging-service.md#queueimage) | None in code | — | Call proceeds |
| [`ReserveImageId`](contracts/imaging-service.md#reserveimageid) | None in code | — | Call proceeds |
| [`SendFax`](contracts/fax-service.md#sendfax) | None in code | — | Call proceeds |
| [`IsAuthorized`](contracts/security-service.md#isauthorized) | None in code | — | Call proceeds |
| [`IsAuthorized2`](contracts/security-service.md#isauthorized2) | None in code | — | Call proceeds |
| [`GetUser`](contracts/security-service.md#getuser) | None in code | — | Call proceeds |
| [`GetSaifUserByUserId`](contracts/security-service.md#getsaifuserbyuserid) | None in code | — | Call proceeds |
| [`GetSaifUserByEmployeeId`](contracts/security-service.md#getsaifuserbyemployeeid) | None in code | — | Call proceeds |
| [`GetUserIdForEmployeeId`](contracts/security-service.md#getuseridforemployeeid) | None in code | — | Call proceeds |
| [`GetExternalUser`](contracts/security-service.md#getexternaluser) | None in code | — | Call proceeds |
| [`GetExternalUserByCustomerId`](contracts/security-service.md#getexternaluserbycustomerid) | None in code | — | Call proceeds |
| [`GetInjuredWorkerUserByCustomerId`](contracts/security-service.md#getinjuredworkeruserbycustomerid) | None in code | — | Call proceeds |
| [`GetUsersByGroup`](contracts/security-service.md#getusersbygroup) | None in code | — | Call proceeds |

## Secrets handling

| Secret | Location | Retrieved by | Consumed by | Notes |
| --- | --- | --- | --- | --- |
| `LOG_REQUEST_DETAILS` | `Web.config` `appSettings` | `ConfigurationManager.AppSettings["LOG_REQUEST_DETAILS"]` inside an empty `try`/`catch` | `SecurityService` | Read into `_logRequestDetails` but never consulted — a dead kill switch (see [Tech debt](tech-debt.md)) |
| `LDAP_EXTERNAL_KEY` | `Web.config` `appSettings` | `ConfigurationManager.AppSettings["LDAP_EXTERNAL_KEY"]` inside an empty `try`/`catch` | `SecurityService` | Names the environment key holding the external LDAP host; falls back to a literal default when unresolved — not reproduced here |
| Application-pool credentials | Pipeline variable group `Architecture.ServiceAccounts.IIS` (`$(wcf.svc.username)` / `$(wcf.svc.password)`) | Release pipeline | IIS application pool identity | Not present in this repository as literal values — not reproduced here |

No credential literal appears in any configuration file in this host.

## Security findings

- **`FileService` accepts arbitrary paths across the network boundary with no authorization.** `FileService` is registered at the route `FileService` and exposes five operations — `ReadFile`, `ReadEntireFile`, `WriteFile`, `DeleteFile`, `Length` — that each take a caller-supplied path string and act on it directly via `System.IO`, with no allow-list, no `Path.GetFullPath` containment check, and no caller-identity check. Any caller that can reach the route can therefore name any path the IIS application-pool identity can reach, including UNC paths. `includeExceptionDetailInFaults="true"` additionally returns filesystem exception detail — including resolved paths — to the caller on failure. See [Tech debt](tech-debt.md).
- **`SecurityService` logs full request detail on every call, unconditionally.** `SecurityService.LogRequestDetails` writes a JSON fragment containing the operation name and its arguments — including `userId`, `employeeId`, `customerId`, `groupName`, and `role` — into the IIS log via `HttpContext.Current.Response.AppendToLog(msg)`, called at the top of every `SecurityService` operation. The `LOG_REQUEST_DETAILS` appSetting that appears intended to gate this is read into a field but never consulted, so the kill switch has no effect. See [Tech debt](tech-debt.md).
- **`ldap_external` is a mutable `private static` field.** It is mutated from an instance constructor on every request and read by the `static` method `IsAuthorizedExternal`; concurrent requests share and overwrite it, a cross-request shared-state hazard on the external authorization path. See [Tech debt](tech-debt.md).
- `ErrorLogErrorHandler` logs `error.GetErrorLogFormattedException()` together with the failing method signature and the calling user; exception messages originating from `FileService` and `PreferenceService` can contain caller-supplied path and user values, so those values reach the central exception log (see [Faults and error pipeline](faults.md)).

## Notes
- Statements marked with `*` in this document set are inferred from surrounding evidence rather than stated directly in source.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Activation and hosting](activation-hosting.md)
- [Operations](operations.md)
- [Faults and error pipeline](faults.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Data model](data-model.md)
- [Operational runbook](operational-runbook.md)
- [Tech debt](tech-debt.md)

## Sources
- src/WCF/SAIF.Services.WCF.Host/Services/FileService.cs
- src/WCF/SAIF.Services.WCF.Host/Services/Security/SecurityService.cs
- src/WCF/SAIF.Services.WCF.Host/Services/Security/AuthorizationCacheService.cs
- src/WCF/SAIF.Services.WCF.Host/Web.Base.config
- pipelines/release.yml
